package s

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"strconv"
	"time"
)

var (
	OPT_SHOW_PROGRESS = 1
)

type Pip struct {
	Reader  io.Reader
	Writeer io.Writer
	Size    int64
	err     error
}

func (self Str) OpenAsWriter(opt ...int) *Pip {
	mode := os.O_APPEND | os.O_CREATE | os.O_WRONLY

	if opt != nil && opt[0] == M_NEW {
		mode = os.O_TRUNC | os.O_CREATE | os.O_WRONLY
	}

	fp, err := os.OpenFile(self.String(), mode, os.ModePerm)
	if err != nil {
		log.Println(err)
		return &Pip{
			err: err,
		}
	}
	return &Pip{
		Writeer: fp,
	}
}

func (self Str) OpenAsReader() *Pip {
	mode := os.O_RDONLY

	fp, err := os.OpenFile(self.String(), mode, os.ModePerm)
	if err != nil {
		log.Println(err)
		return nil
	}
	return &Pip{
		Reader: fp,
	}
}

func (p *Pip) Copy(dst any, showProgressOrTmp ...any) (*Pip, error) {
	if dstFname, ok := dst.(string); ok {
		pipDst := Str(dstFname).OpenAsWriter(M_NEW)
		if pipDst == nil {
			log.Fatal("open faild:", dstFname)
		}
		if pipDst.err != nil {
			// panic(pipDst.err)
			return p, pipDst.err
		} else {
			p.Writeer = pipDst.Writeer
			pipDst = nil
		}
	} else if dstFile, ok := dst.(io.Writer); ok {
		p.Writeer = dstFile
	} else if dstPip, ok := dst.(*Pip); ok {
		if dstPip.err != nil {
			return p, dstPip.err
		} else if dstPip.Writeer != nil {
			p.Writeer = dstPip.Writeer
		}
	} else {
		return p, fmt.Errorf("not support type")
	}
	buf := MakeBytes(1024)
	key := RandomStr(5)
	var err error
	if showProgressOrTmp != nil {
		showPregress := false
		tmp := ""

		for _, opt := range showProgressOrTmp {
			if show, ok := opt.(bool); ok {
				if show {
					showPregress = true
				}
			} else if tmpS, ok := opt.(string); ok {
				showPregress = true
				tmp = tmpS
			}
		}
		if showPregress {
			if p.Size > 0 {
				size := p.Size
				_, err = p.CopyBuffer(p.Writeer, p.Reader, buf.Bytes(), func(usedSec float64, written int64, finish bool) {

					Str(tmp).BarShowSize(key, usedSec, written, finish, size)
				})

			} else {
				_, err = p.CopyBuffer(p.Writeer, p.Reader, buf.Bytes(), func(usedSec float64, written int64, finish bool) {

					Str(tmp).BarShowSize(key, usedSec, written, finish)
				})

			}

		}

	} else {

		_, err = p.CopyBuffer(p.Writeer, p.Reader, buf.Bytes(), nil)

	}
	return p, err
}

func AsPip(raw any, size ...int64) *Pip {
	p := new(Pip)

	if reader, ok := raw.(io.Reader); ok {
		p.Reader = reader
	}
	if writer, ok := raw.(io.Writer); ok {
		p.Writeer = writer
	}

	if size != nil {
		p.Size = size[0]
	}
	if p.Reader == nil && p.Writeer == nil {

		R := reflect.ValueOf(raw)

		if body := R.Elem().FieldByName("Body"); !body.IsNil() {
			if canreader, ok := body.Interface().(io.ReadCloser); ok {
				// headIn := R.Elem().FieldByName("Header")
				// fmt.Println(headIn.Type().Name())

				if header := R.Elem().FieldByName("Header"); !header.IsNil() {
					// fmt,
					if getfu := header.MethodByName("Get"); !getfu.IsNil() {
						getf := getfu.Interface().(func(name string) string)
						// fmt.Println("get func")
						lensStr := getf("Content-Length")
						// fmt.Println("ss:", lensStr)

						if len(lensStr) > 0 {
							if lens, err := strconv.ParseInt(lensStr, 10, 64); err == nil {
								p.Size = lens
								// fmt.Println("ok")
							} else {
								// fmt.Println("err parse !")
							}
						}
					}
					// if maps, ok := header.Interface().(map[string][]any); ok {
					// 	fmt.Println(maps)
					// 	if lensStr, ok := maps["Content-Length"]; ok && len(lensStr) > 0 {
					// 		fmt.Println(lensStr)

					// 		if lens, err := strconv.ParseInt(lensStr[0].(string), 10, 64); err == nil {
					// 			p.Size = lens
					// 		}
					// 	}
					// }
				}
				p.Reader = canreader
				return p
			}
		}
		log.Println("nothing !")
		p = nil
		return nil
	}
	return p
}

func (pip *Pip) Close() error {
	var err error
	if canclosed, ok := pip.Reader.(io.Closer); ok {
		err = canclosed.Close()
	}
	if canclosed, ok := pip.Writeer.(io.Closer); ok {
		err = canclosed.Close()
	}
	return err
}

func (pip *Pip) Write(buf []byte) (written int, err error) {
	if pip.Writeer == nil {
		return 0, fmt.Errorf("no Writeer set!")
	}
	return pip.Writeer.Write(buf)
}

func (pip *Pip) Read(buf []byte) (readed int, err error) {
	if pip.Reader == nil {
		return 0, fmt.Errorf("no Reader set!")
	}
	return pip.Reader.Read(buf)
}

func (pip *Pip) Pipe(other io.ReadWriter, showProgress ...any) error {
	otherPip := AsPip(other)
	go pip.Copy(otherPip, showProgress...)
	_, err := otherPip.Copy(pip, showProgress...)
	pip.Close()
	otherPip.Close()
	return err
}

func (pip *Pip) CopyBuffer(dst io.Writer, src io.Reader, buf []byte, intervalFunc func(usedSec float64, written int64, finish bool)) (written int64, err error) {
	if buf != nil && len(buf) == 0 {
		panic("empty buffer in CopyBuffer")
	}
	return pip.copyBuffer(dst, src, buf, intervalFunc)

}

func (pip *Pip) copyBuffer(dst io.Writer, src io.Reader, buf []byte, intervalFunc func(usedSec float64, written int64, finish bool)) (written int64, err error) {
	// If the reader has a WriteTo method, use it to do the copy.
	// Avoids an allocation and a copy.

	tick := time.NewTicker(1 * time.Second)
	startClock := time.Now()
	if buf == nil {
		size := 32 * 1024
		if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N {
			if l.N < 1 {
				size = 1
			} else {
				size = int(l.N)
			}
		}
		buf = make([]byte, size)
	}

LOOPCOPY:
	for {
		select {
		case <-tick.C:
			if intervalFunc != nil {

				intervalFunc(time.Now().Sub(startClock).Seconds(), written, false)
			}
		default:
			nr, er := src.Read(buf)
			if nr > 0 {
				nw, ew := dst.Write(buf[0:nr])
				if nw < 0 || nr < nw {
					nw = 0
					if ew == nil {
						ew = errors.New("invalid write result")
					}
				}
				written += int64(nw)
				if ew != nil {
					err = ew
					break LOOPCOPY
				}
				if nr != nw {
					err = io.ErrShortWrite
					break LOOPCOPY
				}
			}
			if er != nil {
				if er != io.EOF {
					err = er
				}
				break LOOPCOPY
			}

		}
	}

	if intervalFunc != nil {
		intervalFunc(time.Now().Sub(startClock).Seconds(), written, true)
	}
	return written, err
}
